#include "stdafx.h"
#include "stdlib/datetimeformat.hpp"
#include "stdlib/datetimeformatter.hpp"
#include "stdlib/datetimelocal.hpp"
#include "utils/numberformatter.hpp"
using namespace JHCPP::utils;

namespace JHCPP
{
	namespace stdlib
	{
		std::string CDateTimeFormatter::format(const CTimeStamp& timestamp, const std::string& fmt, int timeZoneDifferential)
		{
			CDateTime dateTime(timestamp);
			return format(dateTime, fmt, timeZoneDifferential);
		}

		std::string CDateTimeFormatter::format(const CDateTime& dateTime, const std::string& fmt, int timeZoneDifferential)
		{
			std::string result;
			result.reserve(64);
			append(result, dateTime, fmt, timeZoneDifferential);
			return result;
		}

		std::string CDateTimeFormatter::format(const CLocalDateTime& dateTime, const std::string& fmt)
		{
			return format(dateTime.m_dateTime, fmt, dateTime.m_tzd);
		}

		std::string CDateTimeFormatter::format(const CTimeSpan& timespan, const std::string& fmt)
		{
			std::string result;
			result.reserve(32);
			append(result, timespan, fmt);
			return result;
		}

		void CDateTimeFormatter::append(std::string& str, const CTimeStamp& timestamp, const std::string& fmt, int timeZoneDifferential)
		{
			CDateTime dateTime(timestamp);
			append(str, dateTime, fmt, timeZoneDifferential);
		}

		void CDateTimeFormatter::append(std::string& str, const CDateTime& dateTime, const std::string& fmt, int timeZoneDifferential)
		{
			std::string::const_iterator it  = fmt.begin();
			std::string::const_iterator end = fmt.end();
			while (it != end)
			{
				if (*it == '%')
				{
					if (++it != end)
					{
						switch (*it)
						{
						case 'w': str.append(CDateTimeFormat::WEEKDAY_NAMES[dateTime.dayOfWeek()], 0, 3); break;
						case 'W': str.append(CDateTimeFormat::WEEKDAY_NAMES[dateTime.dayOfWeek()]); break;
						case 'b': str.append(CDateTimeFormat::MONTH_NAMES[dateTime.month() - 1], 0, 3); break;
						case 'B': str.append(CDateTimeFormat::MONTH_NAMES[dateTime.month() - 1]); break;
						case 'd': CNumberFormatter::append0(str, dateTime.day(), 2); break;
						case 'e': CNumberFormatter::append(str, dateTime.day()); break;
						case 'f': CNumberFormatter::append(str, dateTime.day(), 2); break;
						case 'm': CNumberFormatter::append0(str, dateTime.month(), 2); break;
						case 'n': CNumberFormatter::append(str, dateTime.month()); break;
						case 'o': CNumberFormatter::append(str, dateTime.month(), 2); break;
						case 'y': CNumberFormatter::append0(str, dateTime.year() % 100, 2); break;
						case 'Y': CNumberFormatter::append0(str, dateTime.year(), 4); break;
						case 'H': CNumberFormatter::append0(str, dateTime.hour(), 2); break;
						case 'h': CNumberFormatter::append0(str, dateTime.hourAMPM(), 2); break;
						case 'a': str.append(dateTime.isAM() ? "am" : "pm"); break;
						case 'A': str.append(dateTime.isAM() ? "AM" : "PM"); break;
						case 'M': CNumberFormatter::append0(str, dateTime.minute(), 2); break;
						case 'S': CNumberFormatter::append0(str, dateTime.second(), 2); break;
						case 's': CNumberFormatter::append0(str, dateTime.second(), 2); 
							str += '.'; 
							CNumberFormatter::append0(str, dateTime.millisecond()*1000 + dateTime.microsecond(), 6); 
							break;
						case 'i': CNumberFormatter::append0(str, dateTime.millisecond(), 3); break;
						case 'c': CNumberFormatter::append(str, dateTime.millisecond()/100); break;
						case 'F': CNumberFormatter::append0(str, dateTime.millisecond()*1000 + dateTime.microsecond(), 6); break;
						case 'z': tzdISO(str, timeZoneDifferential); break;
						case 'Z': tzdRFC(str, timeZoneDifferential); break;
						default:  str += *it;
						}
						++it;
					}
				}
				else str += *it++;
			}
		}

		void CDateTimeFormatter::append(std::string& str, const CLocalDateTime& dateTime, const std::string& fmt)
		{
			CDateTimeFormatter::append(str, dateTime.m_dateTime, fmt, dateTime.tzd());
		}

		void CDateTimeFormatter::append(std::string& str, const CTimeSpan& timespan, const std::string& fmt)
		{
			std::string::const_iterator it  = fmt.begin();
			std::string::const_iterator end = fmt.end();
			while (it != end)
			{
				if (*it == '%')
				{
					if (++it != end)
					{
						switch (*it)
						{
						case 'd': CNumberFormatter::append(str, timespan.days()); break;
						case 'H': CNumberFormatter::append0(str, timespan.hours(), 2); break;
						case 'h': CNumberFormatter::append(str, timespan.totalHours()); break;
						case 'M': CNumberFormatter::append0(str, timespan.minutes(), 2); break;
						case 'm': CNumberFormatter::append(str, timespan.totalMinutes()); break;
						case 'S': CNumberFormatter::append0(str, timespan.seconds(), 2); break;
						case 's': CNumberFormatter::append(str, timespan.totalSeconds()); break;
						case 'i': CNumberFormatter::append0(str, timespan.milliseconds(), 3); break;
						case 'c': CNumberFormatter::append(str, timespan.milliseconds()/100); break;
						case 'F': CNumberFormatter::append0(str, timespan.milliseconds()*1000 + timespan.microseconds(), 6); break;
						default:  str += *it;
						}
						++it;
					}
				}
				else str += *it++;
			}
		}

		std::string CDateTimeFormatter::tzdISO(int timeZoneDifferential)
		{
			std::string result;
			result.reserve(8);
			tzdISO(result, timeZoneDifferential);
			return result;
		}

		std::string CDateTimeFormatter::tzdRFC(int timeZoneDifferential)
		{
			std::string result;
			result.reserve(8);
			tzdRFC(result, timeZoneDifferential);
			return result;
		}

		void CDateTimeFormatter::tzdISO(std::string& str, int timeZoneDifferential)
		{
			if (timeZoneDifferential != UTC)
			{
				if (timeZoneDifferential >= 0)
				{
					str += '+';
					CNumberFormatter::append0(str, timeZoneDifferential/3600, 2);
					str += ':';
					CNumberFormatter::append0(str, (timeZoneDifferential%3600)/60, 2);
				}
				else
				{
					str += '-';
					CNumberFormatter::append0(str, -timeZoneDifferential/3600, 2);
					str += ':';
					CNumberFormatter::append0(str, (-timeZoneDifferential%3600)/60, 2);
				}
			}
			else str += 'Z';
		}

		void CDateTimeFormatter::tzdRFC(std::string& str, int timeZoneDifferential)
		{
			if (timeZoneDifferential != UTC)
			{
				if (timeZoneDifferential >= 0)
				{
					str += '+';
					CNumberFormatter::append0(str, timeZoneDifferential/3600, 2);
					CNumberFormatter::append0(str, (timeZoneDifferential%3600)/60, 2);
				}
				else
				{
					str += '-';
					CNumberFormatter::append0(str, -timeZoneDifferential/3600, 2);
					CNumberFormatter::append0(str, (-timeZoneDifferential%3600)/60, 2);
				}		
			}
			else str += "GMT";
		}
	}//end of namespace stdlib
}//end of namespace JHCPP
